package com.zl.bootsecurityjwt.common.config;
import com.zl.bootsecurityjwt.common.jwt.JwtTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * 网络安全配置适配器类
 *
 * 作者：周浪
 * 时间：2021-11-12
 */

/**
 * prePostEnabled注解
 * 这个开启后支持Spring EL表达式 算是蛮厉害的。如果没有访问方法的权限，会抛出AccessDeniedException。
 *
 * 主要注解
 *
 * @PreAuthorize --适合进入方法之前验证授权
 *
 * @PostAuthorize --检查授权方法之后才被执行
 *
 * @PostFilter --在方法执行之后执行，而且这里可以调用方法的返回值，然后对返回值进行过滤或处理或修改并返回
 *
 * @PreFilter --在方法执行之前执行，而且这里可以调用方法的参数，然后对参数值进行过滤或处理或修改
 * ————————————————
 * JSR-250注解
 * 主要注解
 *
 * @DenyAll
 * @RolesAllowed
 * @PermitAll
 * 这里面@DenyAll 和 @PermitAll 相信就不用多说了 代表拒绝和通过。
 *
 * @RolesAllowed 使用示例
 *
 * @RolesAllowed({"USER", "ADMIN"})
 *
 * 代表标注的方法只要具有USER, ADMIN任意一种权限就可以访问。这里可以省略前缀ROLE_，实际的权限可能是ROLE_ADMIN
 * ————————————————
 * securedEnabled注解
 * 主要注解
 *
 *  @Secured
 *  @Secured在方法上指定安全性要求
 *
 *  可以使用@Secured在方法上指定安全性要求 角色/权限等 只有对应 角色/权限 的用户才可以调用这些方法。 如果有人试图调用一个方法，但是不拥有所需的 角色/权限，那会将会拒绝访问将引发异常。
 *
 *  @Secured使用示例
 *
 *  @Secured("ROLE_ADMIN")
 *  @Secured({ "ROLE_DBA", "ROLE_ADMIN" })
 *
 *  还有一点就是@Secured,不支持Spring EL表达式
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true)
public class JwtSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;

    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Autowired
    private JwtTokenFilter jwtTokenFilter;


    /**
     *  配置的是认证信息, AuthenticationManagerBuilder 这个类,就是AuthenticationManager的建造者,
     *  我们只需要向这个类中, 配置用户信息, 就能生成对应的AuthenticationManager, 这个类也提过,是用户身份的管理者,
     *  是认证的入口, 因此,我们需要通过这个配置,想security提供真实的用户身份。 如果我们是使用UserDetailsService来配置用户身份的话
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 密码接口
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 我认为就是配置Security的认证策略, 每个模块配置使用and结尾。
     * authorizeRequests()配置路径拦截，表明路径访问所对应的权限，角色，认证信息。
     * formLogin()对应表单认证相关的配置
     * logout()对应了注销相关的配置
     * httpBasic()可以配置basic登录
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                // 权限不足处理类
                .exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .antMatchers("/auth/login").permitAll()
                .anyRequest().authenticated();
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        http.headers().cacheControl();
    }

    /**
     * 请求token校验拦截
     * @return
     */
    @Bean
    public JwtTokenFilter authenticationTokenFilterBean() {
        return new JwtTokenFilter();
    }


    @Autowired
    public void WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler,
                                  JwtTokenFilter jwtTokenFilter) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.jwtTokenFilter = jwtTokenFilter;
    }
    //自带权限验证暂时没用
    /*@Autowired
    public void WebSecurityConfig(JwtAuthenticationEntryPoint unauthorizedHandler,
                                  @Qualifier("RestAuthenticationAccessDeniedHandler") AccessDeniedHandler accessDeniedHandler,
                                  JwtTokenFilter jwtTokenFilter) {
        this.unauthorizedHandler = unauthorizedHandler;
        this.accessDeniedHandler = accessDeniedHandler;
        this.jwtTokenFilter = jwtTokenFilter;
    }*/


    /**
     * 认证管理器
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }



}